The Java Virtual Machine has a method area that is shared among all Java Virtual Machine threads. The method area is analogous to the storage area for compiled code of a conventional language or analogous to the “text” segment in an operating system process. It stores per-class structures such as the run-time constant pool, field and method data, and the code for methods and constructors, including the special methods (§2.9) used in class and instance initialization and interface initialization.
The method area is created on virtual machine start-up. Although the method area is logically part of the heap, simple implementations may choose not to either garbage collect or compact it. This specification does not mandate the location of the method area or the policies used to manage compiled code. The method area may be of a fixed size or may be expanded as required by the computation and may be contracted if a larger method area becomes unnecessary. The memory for the method area does not need to be contiguous.
A Java Virtual Machine implementation may provide the programmer or the user control over the initial size of the method area, as well as, in the case of a varying-size method area, control over the maximum and minimum method area size.
The following exceptional condition is associated with the method area:
If memory in the method area cannot be made available to satisfy an allocation request, the Java Virtual Machine throws an OutOfMemoryError.
// 堆 new String("a") new String("b") new String("ab") Strings2= s.intern(); // 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回 // 如果没有放入,则 s2 为 串池中的 字符串,而 s 为堆中的字符串 s2 != s System.out.println( s2 == x); System.out.println( s == x ); } }
Enables the use of a policy that limits the proportion of time spent by the JVM on GC before an OutOfMemoryError exception is thrown. This option is enabled, by default and the parallel GC will throw an OutOfMemoryError if more than 98% of the total time is spent on garbage collection and less than 2% of the heap is recovered. When the heap is small, this feature can be used to prevent applications from running for long periods of time with little or no progress. To disable this option, specify -XX:-UseGCOverheadLimit.
The Eclipse Memory Analyzer is a fast and feature-rich Java heap analyzer that helps you find memory leaks and reduce memory consumption.
Use the Memory Analyzer to analyze productive heap dumps with hundreds of millions of objects, quickly calculate the retained sizes of objects, see who is preventing the Garbage Collector from collecting objects, run a report to automatically extract leak suspects.
publicstaticvoidmain(String[] args)throws IOException { /*List<byte[]> list = new ArrayList<>(); for (int i = 0; i < 5; i++) { list.add(new byte[_4MB]); } System.in.read();*/ soft(); }
publicstaticvoidsoft() { // list --> SoftReference --> byte[]
List<SoftReference<byte[]>> list = newArrayList<>(); for (inti=0; i < 5; i++) { SoftReference<byte[]> ref = newSoftReference<>(newbyte[_4MB]); System.out.println(ref.get()); list.add(ref); System.out.println(list.size()); } System.out.println("循环结束:" + list.size()); for (SoftReference<byte[]> ref : list) { System.out.println(ref.get()); } } }
Sets the initial and maximum size (in bytes) of the heap for the young generation (nursery). GC is performed in this region more often than in other regions. If the size for the young generation is too small, then a lot of minor garbage collections are performed. If the size is too large, then only full garbage collections are performed, which can take a long time to complete. Oracle recommends that you keep the size for the young generation greater than 25% and less than 50% of the overall heap size.
Desired survivor size 48286924 bytes, new threshold 10 (max 10) - age 1: 28992024 bytes, 28992024 total - age 2: 1366864 bytes, 30358888 total - age 3: 1425912 bytes, 31784800 total ...
ClassFile { u4 magic; // 前4字节 u2 minor_version; // 接下来2字节 u2 major_version; u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1]; u2 access_flags; // 访问修饰 u2 this_class; //类名 包名 u2 super_class; // 父类 u2 interfaces_count; // 接口信息 u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_count]; u2 attributes_count; // 类附加的属性信息 attribute_info attributes[attributes_count]; }
魔数
Magic Number -> A constant numerical or text value used to identify a file format。
publicfinalclassSexextendsEnum<Sex> { publicstaticfinal Sex MALE; publicstaticfinal Sex FEMALE; privatestaticfinal Sex[] $VALUES; static { MALE = newSex("MALE", 0); FEMALE = newSex("FEMALE", 1); $VALUES = newSex[]{MALE, FEMALE}; } /** * Sole constructor. Programmers cannot invoke this constructor. * It is for use by code emitted by the compiler in response to * enum type declarations. * * @param name - The name of this enum constant, which is the identifier * used to declare it. * @param ordinal - The ordinal of this enumeration constant (its position * in the enum declaration, where the initial constant is assigned */ privateSex(String name, int ordinal) { super(name, ordinal); } publicstatic Sex[] values() { return $VALUES.clone(); } publicstatic Sex valueOf(String name) { return Enum.valueOf(Sex.class, name); } }
java.lang.ArithmeticException: / by zero at test.Test6.main(Test6.java:7) Suppressed: java.lang.Exception: close 异常 at test.MyResource.close(Test6.java:18) at test.Test6.main(Test6.java:6)
如以上代码所示,两个异常信息都不会丢。异常丢失见上文
方法重写时的桥接方法
方法重写时对返回值分两种情况:
父子类的返回值完全一致
子类返回值可以是父类返回值的子类(比较绕口,见下面的例子)
1 2 3 4 5 6 7 8 9 10 11 12 13
classA { public Number m() { return1; } }
classBextendsA { @Override // 子类 m 方法的返回值是 Integer 是父类 m 方法返回值 Number 的子类 public Integer m() { return2; } }
对于子类,java 编译器会做如下处理:
1 2 3 4 5 6 7 8 9 10
classBextendsA { public Integer m() { return2; } // 此方法才是真正重写了父类 public Number m() 方法 public synthetic bridge Number m() { // 调用 public Integer m() return m(); } }
其中桥接方法比较特殊,仅对 java 虚拟机可见,并且与原来的 public Integer m() 没有命名冲突,可以用下面反射代码来验证:
1 2 3
for (Method m : B.class.getDeclaredMethods()) { System.out.println(m); }
会输出:
1 2
public java.lang.Integer test.candy.B.m() public java.lang.Number test.candy.B.m()
E:\git\jvm\out\production\jvm>java cn.itcast.jvm.t5.HelloWorld Error: A JNI error has occurred, please check your installation and try again Exception in thread "main" java.lang.ClassFormatError: Incompatible magic value 3405691578 in class file cn/itcast/jvm/t5/HelloWorld at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:763) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) at java.net.URLClassLoader.defineClass(URLClassLoader.java:467) at java.net.URLClassLoader.access$100(URLClassLoader.java:73) at java.net.URLClassLoader$1.run(URLClassLoader.java:368) at java.net.URLClassLoader$1.run(URLClassLoader.java:362) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:361) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)
@Benchmark publicvoidtest1() { // elements.length 首次读取会缓存起来 -> int[] local for (inti=0; i < elements.length; i++) { // 后续 999 次 求长度 <- local sum += elements[i]; // 1000 次取下标 i 的元素 <- local } }
getstatic run // 线程 t 获取 run true getstatic run // 线程 t 获取 run true getstatic run // 线程 t 获取 run true getstatic run // 线程 t 获取 run true putstatic run // 线程 main 修改 run 为 false, 仅此一次 getstatic run // 线程 t 获取 run false
学生(线程 A)用课本占座,上了半节课,出门了(CPU时间到),回来一看,发现课本没变,说明没有竞争,继续上他的课。如果这期间有其它学生(线程 B)来了,会告知(线程A)有并发访问,线程 A 随即升级为重量级锁,进入重量级锁的流程。而重量级锁就不是那么用课本占座那么简单了,可以想象线程 A 走之前,把座位用一个铁栅栏围起来
Exception in thread "Thread1" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 at java.util.ArrayList.rangeCheck(ArrayList.java:657) at java.util.ArrayList.remove(ArrayList.java:496) at cn.itcast.n6.ThreadUnsafe.method3(TestThreadSafe.java:35) at cn.itcast.n6.ThreadUnsafe.method1(TestThreadSafe.java:26) at cn.itcast.n6.TestThreadSafe.lambda$main$0(TestThreadSafe.java:14) at java.lang.Thread.run(Thread.java:748) // 共享同一个对象中的 list 成员变量,存在线程安全问题
# 测试脚本 for /L %n in (1,1,10) do java -cp".;C:\Users\manyh\.m2\repository\ch\qos\logback\logbackclassic\1.2.3\logback-classic-1.2.3.jar;C:\Users\manyh\.m2\repository\ch\qos\logback\logbackcore\1.2.3\logback-core-1.2.3.jar;C:\Users\manyh\.m2\repository\org\slf4j\slf4japi\1.7.25\slf4j-api-1.7.25.jar" cn.itcast.n4.exercise.ExerciseSell
cmd > jps Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8 12320Jps 22816KotlinCompileDaemon 33200TestDeadLock // JVM 进程 11508 Main 28468 Launcher cmd > jstack 33200 Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8 2018-12-29 05:51:40 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.91-b14 mixed mode): "DestroyJavaVM" #13 prio=5 os_prio=0 tid=0x0000000003525000 nid=0x2f60 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Thread-1" #12 prio=5 os_prio=0 tid=0x000000001eb69000 nid=0xd40 waiting for monitor entry [0x000000001f54f000] java.lang.Thread.State: BLOCKED (on object monitor) at thread.TestDeadLock.lambda$main$1(TestDeadLock.java:28) - waiting to lock <0x000000076b5bf1c0> (a java.lang.Object) - locked <0x000000076b5bf1d0> (a java.lang.Object) at thread.TestDeadLock$$Lambda$2/883049899.run(UnknownSource) at java.lang.Thread.run(Thread.java:745) "Thread-0"#11 prio=5 os_prio=0 tid=0x000000001eb68800 nid=0x1b28 waiting for monitor entry [0x000000001f44f000] java.lang.Thread.State: BLOCKED (on object monitor) at thread.TestDeadLock.lambda$main$0(TestDeadLock.java:15) - waiting to lock <0x000000076b5bf1d0> (a java.lang.Object) - locked <0x000000076b5bf1c0> (a java.lang.Object) at thread.TestDeadLock$$Lambda$1/495053715.run(UnknownSource) at java.lang.Thread.run(Thread.java:745)
// 略去部分输出 Found one Java-level deadlock: ============================= "Thread-1": waiting to lock monitor 0x000000000361d378 (object 0x000000076b5bf1c0, a java.lang.Object), which is held by "Thread-0" "Thread-0": waiting to lock monitor 0x000000000361e768 (object 0x000000076b5bf1d0, a java.lang.Object), which is held by "Thread-1" Java stack information for the threads listed above: =================================================== "Thread-1": at thread.TestDeadLock.lambda$main$1(TestDeadLock.java:28) - waiting to lock <0x000000076b5bf1c0> (a java.lang.Object) - locked <0x000000076b5bf1d0> (a java.lang.Object) at thread.TestDeadLock$$Lambda$2/883049899.run(UnknownSource) at java.lang.Thread.run(Thread.java:745) "Thread-0": at thread.TestDeadLock.lambda$main$0(TestDeadLock.java:15) - waiting to lock <0x000000076b5bf1d0> (a java.lang.Object) - locked <0x000000076b5bf1c0> (a java.lang.Object) at thread.TestDeadLock$$Lambda$1/495053715.run(UnknownSource) at java.lang.Thread.run(Thread.java:745) Found1 deadlock.
检测死锁 –使用 jconsole工具
解决死锁问题 –破坏死锁形成条件
避免死锁,要注意加锁顺序
另外如果由于某个线程进入了死循环,导致其它线程一直等待,对于这种情况 linux 下可以通过 top 先定位到 CPU 占用高的 Java 进程,再利用 top -Hp 进程id 来定位是哪个线程,最后再用 jstack 排查
/*D:\JAVA\jdk1.8_0_211\bin\java.exe -javaagent:D:\JetBrains\ToolboxDownload\apps\IDEA-U\ch-0\223.7571.182\lib\idea_rt.jar=6251:D:\JetBrains\ToolboxDownload\apps\IDEA-U\ch-0\223.7571.182\bin -Dfile.encoding=UTF-8 -classpath D:\JAVA\jdk1.8_0_211\jre\lib\charsets.jar;D:\JAVA\jdk1.8_0_211\jre\lib\deploy.jar;D:\JAVA\jdk1.8_0_211\jre\lib\ext\access-bridge-64.jar;D:\JAVA\jdk1.8_0_211\jre\lib\ext\cldrdata.jar;D:\JAVA\jdk1.8_0_211\jre\lib\ext\dnsns.jar;D:\JAVA\jdk1.8_0_211\jre\lib\ext\jaccess.jar;D:\JAVA\jdk1.8_0_211\jre\lib\ext\jfxrt.jar;D:\JAVA\jdk1.8_0_211\jre\lib\ext\localedata.jar;D:\JAVA\jdk1.8_0_211\jre\lib\ext\nashorn.jar;D:\JAVA\jdk1.8_0_211\jre\lib\ext\sunec.jar;D:\JAVA\jdk1.8_0_211\jre\lib\ext\sunjce_provider.jar;D:\JAVA\jdk1.8_0_211\jre\lib\ext\sunmscapi.jar;D:\JAVA\jdk1.8_0_211\jre\lib\ext\sunpkcs11.jar;D:\JAVA\jdk1.8_0_211\jre\lib\ext\zipfs.jar;D:\JAVA\jdk1.8_0_211\jre\lib\javaws.jar;D:\JAVA\jdk1.8_0_211\jre\lib\jce.jar;D:\JAVA\jdk1.8_0_211\jre\lib\jfr.jar;D:\JAVA\jdk1.8_0_211\jre\lib\jfxswt.jar;D:\JAVA\jdk1.8_0_211\jre\lib\jsse.jar;D:\JAVA\jdk1.8_0_211\jre\lib\management-agent.jar;D:\JAVA\jdk1.8_0_211\jre\lib\plugin.jar;D:\JAVA\jdk1.8_0_211\jre\lib\resources.jar;D:\JAVA\jdk1.8_0_211\jre\lib\rt.jar;E:\Todo\note\SoftwareDocument\Java_Note\JUC\Code\JUC-Code\target\classes;D:\maven-repository\org\projectlombok\lombok\1.18.24\lombok-1.18.24.jar;D:\maven-repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;D:\maven-repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;D:\maven-repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;D:\maven-repository\org\openjdk\jol\jol-core\0.16\jol-core-0.16.jar JUC.chapter_4_Monitor.Test19 02:18:56.846 c.Test19 [t1] - try to obtain lock 02:18:57.854 c.Test19 [main] - interrupt t1 02:18:57.855 c.Test19 [t1] - not lock java.lang.InterruptedException at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222) at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335) at JUC.chapter_4_Monitor.Test19.lambda$main$0(Test19.java:21) at java.lang.Thread.run(Thread.java:748) Process finished with exit code 0*/ }
getstatic run // 线程 t 获取 run true getstatic run // 线程 t 获取 run true getstatic run // 线程 t 获取 run true getstatic run // 线程 t 获取 run true putstatic run // 线程 main 修改 run 为 false, 仅此一次 getstatic run // 线程 t 获取 run false
*** INTERESTING tests Some interesting behaviors observed. This is for the plain curiosity. 2 matching test results. [OK] test.ConcurrencyTest (JVMargs: [-XX:-TieredCompilation]) Observed state OccurrencesExpectationInterpretation 01,729ACCEPTABLE_INTERESTING !!!! 142,617,915ACCEPTABLE ok 45,146,627ACCEPTABLE ok [OK] test.ConcurrencyTest (JVMargs: []) Observed state OccurrencesExpectationInterpretation 01,652ACCEPTABLE_INTERESTING !!!! 146,460,657ACCEPTABLE ok 44,571,072ACCEPTABLE ok
// 自定义原子运算 AtomicIntegerj=newAtomicInteger(2); // update set get System.out.println(j.updateAndGet(x -> x * 10)); System.out.println(j.getAndUpdate(x -> x * 10)); System.out.println(j.get()); } }
19:10:40.859 [Thread-2] c.TestDateParse - {} java.lang.NumberFormatException: For input string:"" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.lang.Long.parseLong(Long.java:601) at java.lang.Long.parseLong(Long.java:631) at java.text.DigitList.getLong(DigitList.java:195) at java.text.DecimalFormat.parse(DecimalFormat.java:2084) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2162) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514) at java.text.DateFormat.parse(DateFormat.java:364) at cn.itcast.n7.TestDateParse.lambda$test1$0(TestDateParse.java:18) at java.lang.Thread.run(Thread.java:748) 19:10:40.859 [Thread-1] c.TestDateParse - {} java.lang.NumberFormatException: empty String at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1842) at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110) at java.lang.Double.parseDouble(Double.java:538) at java.text.DigitList.getDouble(DigitList.java:169) at java.text.DecimalFormat.parse(DecimalFormat.java:2089) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2162) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514) at java.text.DateFormat.parse(DateFormat.java:364) at cn.itcast.n7.TestDateParse.lambda$test1$0(TestDateParse.java:18) at java.lang.Thread.run(Thread.java:748) 19:10:40.857 [Thread-8] c.TestDateParse - SatApr2100:00:00CST1951 19:10:40.857 [Thread-9] c.TestDateParse - SatApr2100:00:00CST1951 19:10:40.857 [Thread-6] c.TestDateParse - SatApr2100:00:00CST1951 19:10:40.857 [Thread-4] c.TestDateParse - SatApr2100:00:00CST1951 19:10:40.857 [Thread-5] c.TestDateParse - MonApr2100:00:00CST178960645 19:10:40.857 [Thread-0] c.TestDateParse - SatApr2100:00:00CST1951 19:10:40.857 [Thread-7] c.TestDateParse - SatApr2100:00:00CST1951 19:10:40.857 [Thread-3] c.TestDateParse - SatApr2100:00:00CST1951
// 可变类在多线程下运行异常问题 /* Exception in thread "Thread-8" Exception in thread "Thread-7" Exception in thread "Thread-1" Exception in thread "Thread-3" Exception in thread "Thread-2" Exception in thread "Thread-0" Exception in thread "Thread-4" Exception in thread "Thread-6" java.lang.NumberFormatException: multiple points at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1890) at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110) at java.lang.Double.parseDouble(Double.java:538) at java.text.DigitList.getDouble(DigitList.java:169) at java.text.DecimalFormat.parse(DecimalFormat.java:2089) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514) at java.text.DateFormat.parse(DateFormat.java:364) */ // 原理 --SimpleDateFormat类 内部状态 和 内部一些成员变量都是可变的,这就会导致在多个线程并发执行时出现线程安全问题 privatestaticvoidtest1() { SimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-dd"); for (inti=0; i < 10; i++) { newThread(() -> { try { log.debug("{}", sdf.parse("1951-04-21")); } catch (ParseException e) { thrownewRuntimeException(e); } }).start(); } }
publicfinalclassString implementsjava.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ privatefinalchar value[]; /** Cache the hash code for the string */ privateint hash; // Default to 0
publicThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
21:59:04.558 c.TestTimer [pool-1-thread-1] - task1 21:59:04.562 c.TestTimer [pool-1-thread-1] - error: java.lang.ArithmeticException: / by zero at cn.itcast.n8.TestTimer.lambda$main$0(TestTimer.java:28) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)
21:54:58.208 c.TestTimer [pool-1-thread-1] - task1 Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero at java.util.concurrent.FutureTask.report(FutureTask.java:122) at java.util.concurrent.FutureTask.get(FutureTask.java:192) at cn.itcast.n8.TestTimer.main(TestTimer.java:31) Caused by: java.lang.ArithmeticException: / by zero at cn.itcast.n8.TestTimer.lambda$main$0(TestTimer.java:28) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)
publicbooleanforce(Runnable o, long timeout, TimeUnit unit)throws InterruptedException { if ( parent.isShutdown() ) thrownewRejectedExecutionException( "Executor not running, can't force a command into the queue" ); returnsuper.offer(o,timeout,unit); //forces the item onto the queue, to be used if the task is rejected }
@Override public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)throws SQLException { returnnull; }
@Override public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)throws SQLException { returnnull; }
@Override public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)throws SQLException { returnnull; }
@Override public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)throws SQLException { returnnull; }
C:\Users\黎明\Desktop>java -jar -Xmx2G benchmarks.jar # VM invoker: D:\JAVA\jre1.8_0_211\bin\java.exe # VM options: -Xmx2G # Warmup: 3 iterations, 1 s each # Measurement: 5 iterations, 1 s each # Threads: 1 thread, will synchronize iterations # Benchmark mode: Average time, time/op # Benchmark: com.itcast.MyBenchmark.a
# VM invoker: D:\JAVA\jre1.8_0_211\bin\java.exe # VM options: -Xmx2G # Warmup: 3 iterations, 1 s each # Measurement: 5 iterations, 1 s each # Threads: 1 thread, will synchronize iterations # Benchmark mode: Average time, time/op # Benchmark: com.itcast.MyBenchmark.b
This is ApacheBench, Version 2.3 <$Revision: 1843412 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking localhost (be patient) Completed 5000 requests Completed 10000 requests Completed 15000 requests Completed 20000 requests Finished 24706 requests Server Software: Server Hostname: localhost Server Port: 8080 Document Path: /test Document Length: 2 bytes Concurrency Level: 10 Time taken for tests: 10.005 seconds Complete requests: 24706 Failed requests: 0 Total transferred: 3311006 bytes HTML transferred: 49418 bytes Requests per second: 2469.42 [#/sec] (mean) Time per request: 4.050 [ms] (mean) Time per request: 0.405 [ms] (mean, across all concurrent requests) Transfer rate: 323.19 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 1.4 0 16 Processing: 0 4 7.6 0 323 Waiting: 0 3 6.9 0 323 Total: 0 4 7.6 0 323 Percentage of the requests served within a certain time (ms) 50% 0 66% 2 75% 8 80% 8 90% 10 95% 16 98% 16 99% 16 100% 323 (longest request)
// 可以重排的例子 inta=10; // 指令1 intb=20; // 指令2 System.out.println( a + b );
// 不能重排的例子 inta=10; // 指令1 intb= a - 5; // 指令2
参考文档
参考: Scoreboarding and the Tomasulo algorithm (which is similar to scoreboarding but makes use of register renaming) are two of the most common techniques for implementing out-of-order execution and instruction-level parallelism.
支持流水线的处理器
现代 CPU 支持多级指令流水线,例如支持同时执行 取指令 - 指令译码 - 执行指令 - 内存访问 - 数据写回 的处理器,就可以称之为五级指令流水线。这时 CPU 可以在一个时钟周期内,同时运行五条指令的不同阶段(相当于一 条执行时间最长的复杂指令),IPC = 1,本质上,流水线技术并不能缩短单条指令的执行时间,但它变相地提高了 指令地吞吐率。
// ㈠ AQS 继承过来的方法, 方便阅读, 放在此处 privatevoidsetHeadAndPropagate(Node node, int propagate) { Nodeh= head; // Record old head for check below // 设置自己为 head setHead(node);
// propagate 表示有共享资源(例如共享读锁或信号量) // 原 head waitStatus == Node.SIGNAL 或 Node.PROPAGATE // 现在 head waitStatus == Node.SIGNAL 或 Node.PROPAGATE if (propagate > 0 || h == null || h.waitStatus < 0 || (h = head) == null || h.waitStatus < 0) { Nodes= node.next; // 如果是最后一个节点或者是等待共享读锁的节点 if (s == null || s.isShared()) { // 进入 ㈡ doReleaseShared(); } } }
// ㈡ AQS 继承过来的方法, 方便阅读, 放在此处 privatevoiddoReleaseShared() { // 如果 head.waitStatus == Node.SIGNAL ==> 0 成功, 下一个节点 unpark // 如果 head.waitStatus == 0 ==> Node.PROPAGATE, 为了解决 bug, 见后面分析 for (;;) { Nodeh= head; // 队列还有节点 if (h != null && h != tail) { intws= h.waitStatus; if (ws == Node.SIGNAL) { if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) continue; // loop to recheck cases // 下一个节点 unpark 如果成功获取读锁 // 并且下下个节点还是 shared, 继续 doReleaseShared unparkSuccessor(h); } elseif (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) continue; // loop on failed CAS } if (h == head) // loop if head changed break; } } }
// 上述可见没有插入 ID /* Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@518ddd3b] was not registered for synchronization because synchronization is not active JDBC Connection [HikariProxyConnection@1720760826 wrapping com.mysql.cj.jdbc.ConnectionImpl@6c8dbf56] will not be managed by Spring ==> Preparing: INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? ) ==> Parameters: 1580171623430950913(Long), Line(String), 18(Integer), 1031282691@qq.com(String) <== Updates: 1 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@518ddd3b] 1 User(id=1580171623430950913, name=Line, age=18, email=1031282691@qq.com) */ // 但是从结果可知 mybatis-plus 会自动生成 ID
intret= userMapper.insert(user); System.out.println(ret); // 打印受影响的行数 System.out.println(user); // 自动生成的 ID 会自动回填到 user 对象的属性中 } }
/* 日志: Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2d85fb64] was not registered for synchronization because synchronization is not active JDBC Connection [HikariProxyConnection@513241240 wrapping com.mysql.cj.jdbc.ConnectionImpl@630390b9] will not be managed by Spring ==> Preparing: UPDATE user SET name=? WHERE id=? ==> Parameters: 李宁(String), 1580171623430950913(Long) <== Updates: 1 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2d85fb64] 1 */ // 注意 虽然 userMapper.updateById 调用的 updateById 方法 但是 其输入参数 为一个 T 泛型 即你创建的 实体类 // 根据多次 测试 可以很明显的 看出 mybatis-plus 可以根据 所给对象的参数 通过条件自动拼接动态 sql intret= userMapper.updateById(user); System.out.println(ret); }
/* 日志: Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2d85fb64] was not registered for synchronization because synchronization is not active JDBC Connection [HikariProxyConnection@513241240 wrapping com.mysql.cj.jdbc.ConnectionImpl@630390b9] will not be managed by Spring ==> Preparing: UPDATE user SET name=? WHERE id=? ==> Parameters: 李宁(String), 1580171623430950913(Long) <== Updates: 1 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2d85fb64] 1 */ // 注意 虽然 userMapper.updateById 调用的 updateById 方法 但是 其输入参数 为一个 T 泛型 即你创建的 实体类 // 根据多次 测试 可以很明显的 看出 mybatis-plus 可以根据 所给对象的参数 通过条件自动拼接动态 sql intret= userMapper.updateById(user); System.out.println(ret); }
@Test publicvoidtestOptimisticLocker() { // 测试乐观锁 成功 update // 查询用户信息 Useruser= userMapper.selectById(1L); // 设置用户信息 user.setName("Lixiaofeng"); user.setEmail("33069987@asiainfo.com"); // 执行更新操作 userMapper.updateById(user); /* 执行结果 日志 Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1a1cc163] was not registered for synchronization because synchronization is not active JDBC Connection [HikariProxyConnection@1320809135 wrapping com.mysql.cj.jdbc.ConnectionImpl@4a481728] will not be managed by Spring ==> Preparing: SELECT id,name,age,email,version,create_time,update_time FROM user WHERE id=? ==> Parameters: 1(Long) <== Columns: id, name, age, email, version, create_time, update_time <== Row: 1, Lixiaofeng, 18, 33069987@asiainfo.com, 1, 2022-10-12 22:28:33, 2022-10-13 09:13:19 <== Total: 1 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1a1cc163] Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7327a447] was not registered for synchronization because synchronization is not active 2022-10-13 09:17:39.634 INFO 19220 --- [ main] c.e.m.handler.MyMetaObjectHandler : start update fill.... JDBC Connection [HikariProxyConnection@876669389 wrapping com.mysql.cj.jdbc.ConnectionImpl@4a481728] will not be managed by Spring ==> Preparing: UPDATE user SET name=?, age=?, email=?, version=?, create_time=?, update_time=? WHERE id=? AND version=? ==> Parameters: Lixiaofeng(String), 18(Integer), 33069987@asiainfo.com(String), 2(Integer), 2022-10-12 22:28:33.0(Timestamp), 2022-10-13 09:17:39.634(Timestamp), 1(Long), 1(Integer) <== Updates: 1 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7327a447] 可以清晰的看到 插件 帮我们在 update 的最后 添加了 AND version=? 同时 传入了值 也可以看到 使用 MyBatis-Plus 做 Select 查询语句 会自动回显 到对象属性上 */ }
// 测试通过 id 批量逻辑删除 userMapper.deleteBatchIds(Arrays.asList(2L, 3L));
// 测试通过 map 逻辑删除 HashMap<String, Object> map = newHashMap<>(); map.put("name", "Billie"); // TODO 在理解 自增id的问题 SQLyog 写入数据条目 id 后会被改变 故而研究 MyBatis-Plus 实现数据时 改变 ID 的流程时间 userMapper.deleteByMap(map);
/* 执行结果日志 Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4eb9f2af] was not registered for synchronization because synchronization is not active JDBC Connection [HikariProxyConnection@268258490 wrapping com.mysql.cj.jdbc.ConnectionImpl@5f1483fd] will not be managed by Spring ==> Preparing: UPDATE user SET deleted=1 WHERE id=? AND deleted=0 ==> Parameters: 1(Long) <== Updates: 1 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4eb9f2af] Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@65bad087] was not registered for synchronization because synchronization is not active JDBC Connection [HikariProxyConnection@1251133097 wrapping com.mysql.cj.jdbc.ConnectionImpl@5f1483fd] will not be managed by Spring ==> Preparing: UPDATE user SET deleted=1 WHERE id IN ( ? , ? ) AND deleted=0 ==> Parameters: 2(Long), 3(Long) <== Updates: 2 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@65bad087] Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4cb702ce] was not registered for synchronization because synchronization is not active JDBC Connection [HikariProxyConnection@397639322 wrapping com.mysql.cj.jdbc.ConnectionImpl@5f1483fd] will not be managed by Spring ==> Preparing: UPDATE user SET deleted=1 WHERE name = ? AND deleted=0 ==> Parameters: Billie(String) <== Updates: 1 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4cb702ce] 可以直观看出 虽然实际调用的是 delete 方法 但是在配置了逻辑删除后 实际上执行的是 update sql 同时 配置逻辑查询后 再执行查询 会自动过滤被删除的字段 --MyBatis-Plus 的强大之处 */ }
/* 配置 SQL 性能检测后 的执行结果日志 Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7c37f145] was not registered for synchronization because synchronization is not active JDBC Connection [HikariProxyConnection@1955226954 wrapping com.mysql.cj.jdbc.ConnectionImpl@1d8b0500] will not be managed by Spring ==> Preparing: SELECT id,name,age,email,version,create_time,update_time,deleted FROM user WHERE deleted=0 ==> Parameters: <== Columns: id, name, age, email, version, create_time, update_time, deleted <== Row: 4, Sandy, 21, test4@baomidou.com, 1, 2022-10-12 22:28:33, 2022-10-12 22:28:33, 0 <== Row: 6, kaungshen, 12, 983443816@qq.com, 1, 2022-10-12 22:28:33, 2022-10-12 22:28:33, 0 <== Total: 2 Time:16 ms - ID:com.example.mybatisplustest.mapper.UserMapper.selectList Execute SQL: SELECT id, name, age, email, version, create_time, update_time, deleted FROM user WHERE deleted=0 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7c37f145] org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: The SQL execution time is too large, please optimize ! ### The error may exist in com/example/mybatisplustest/mapper/UserMapper.java (best guess) ### The error may involve com.example.mybatisplustest.mapper.UserMapper.selectList ### The error occurred while handling results ### SQL: SELECT id,name,age,email,version,create_time,update_time,deleted FROM user WHERE deleted=0 ### Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: The SQL execution time is too large, please optimize ! Caused by: org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: The SQL execution time is too large, please optimize ! ### The error may exist in com/example/mybatisplustest/mapper/UserMapper.java (best guess) ### The error may involve com.example.mybatisplustest.mapper.UserMapper.selectList ### The error occurred while handling results ### SQL: SELECT id,name,age,email,version,create_time,update_time,deleted FROM user WHERE deleted=0 ### Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: The SQL execution time is too large, please optimize ! 可以清晰看到 格式化 后的 SQL 语句 以及对比 SQL 执行的时间 */ }
userMapper.selectList(wrapper).forEach(System.out::println); /* 查询结果日志 Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@631cb129] was not registered for synchronization because synchronization is not active JDBC Connection [HikariProxyConnection@1878045132 wrapping com.mysql.cj.jdbc.ConnectionImpl@3b55dd15] will not be managed by Spring ==> Preparing: SELECT id,name,age,email,version,create_time,update_time,deleted FROM user WHERE deleted=0 AND name IS NOT NULL AND email IS NOT NULL AND age >= ? ==> Parameters: 18(Integer) <== Columns: id, name, age, email, version, create_time, update_time, deleted <== Row: 4, Sandy, 21, test4@baomidou.com, 1, 2022-10-12 22:28:33, 2022-10-12 22:28:33, 0 <== Row: 15801716234310, Liyang, 28, 1031222691@qq.com, 1, 2022-10-13 11:57:01, 2022-10-13 11:57:01, 0 <== Row: 15801716234311, Lizhengzuo, 38, 1031222691@qq.com, 1, 2022-10-13 11:57:17, 2022-10-13 11:57:17, 0 <== Total: 3 Time:14 ms - ID:com.example.mybatisplustest.mapper.UserMapper.selectList Execute SQL: SELECT id, name, age, email, version, create_time, update_time, deleted FROM user WHERE deleted=0 AND name IS NOT NULL AND email IS NOT NULL AND age >= 18 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@631cb129] User(id=4, name=Sandy, age=21, email=test4@baomidou.com, version=1, createTime=Wed Oct 12 22:28:33 CST 2022, updateTime=Wed Oct 12 22:28:33 CST 2022, deleted=0) User(id=15801716234310, name=Liyang, age=28, email=1031222691@qq.com, version=1, createTime=Thu Oct 13 11:57:01 CST 2022, updateTime=Thu Oct 13 11:57:01 CST 2022, deleted=0) User(id=15801716234311, name=Lizhengzuo, age=38, email=1031222691@qq.com, version=1, createTime=Thu Oct 13 11:57:17 CST 2022, updateTime=Thu Oct 13 11:57:17 CST 2022, deleted=0) */ }
publicArrayList(E[] arr) { //data不能直接引用外部传入的数组arr //否则外部对arr的修改会引起ArrayList内部的一些问题 //data是ArrayList内部的数据 if (arr == null || arr.length == 0) { thrownewIllegalArgumentException("arr can not be null"); } data = (E[]) newObject[DEFAULT_CAPACITY]; for (inti=0; i < arr.length; i++) { add(arr[i]); } }
//扩容或缩容 操作 不应该向外界提供 privatevoidresize(int newLen) { E[] newData = (E[]) newObject[newLen]; for (inti=0; i < size; i++) { newData[i] = data[i]; } data = newData; }
@Override publicvoidremove(E element) {//删除指定元素 仅删除第一次出现的 E or// 删除所有指定元素 // 仅删除第一次出现的 E intindex= indexOf(element); if (index != -1) { remove(index); } }
@Override public E remove(int index) { if (index < 0 || index >= size) { thrownewIllegalArgumentException("remove index out of range"); } //先保存要删除的值 Eret= data[index];
//移动元素 for (inti= index + 1; i < size; i++) { data[i - 1] = data[i]; } size--;
@Override public E get(int index) { if (index < 0 || index >= size) { thrownewIllegalArgumentException("get index out of range "); } return data[index]; }
@Override public E set(int index, E element) { if (index < 0 || index >= size) { thrownewIllegalArgumentException("set index out of range "); } Eret= data[index]; data[index] = element; return ret; }
@Override publicvoidsort(Comparator<E> c) { /* list.sort((o1, o2) -> o1 - o2); */ if (c == null) { thrownewIllegalArgumentException("comparator can not be null"); }
@Override public E next() { return it.next(); } } }
队列
队列性质
FIFO
队列的入队时间复杂度为 O(1) 出队时间复杂度为O(n)
底层数据结构
线性表 ArrayList
手写代码实现
Queue
1 2 3 4 5 6 7 8 9 10 11 12 13
package P1.Interface;
publicinterfaceQueue<E> extendsIterable<E>{ //入队 publicvoidoffer(E element); //出队 public E poll(); //查看队首元素 public E element(); publicbooleanisEmpty(); publicvoidclear(); publicintsize(); }
@Override public E pop() { if (isEmpty()) { thrownewIllegalArgumentException("stack is null"); } for (inti=0; i < queueA.size() - 1; i++) { Etmp= queueA.poll(); queueA.offer(tmp); } return queueA.poll(); }
@Override public E peek() { if (isEmpty()) { thrownewIllegalArgumentException("stack is null"); } for (inti=0; i < queueA.size() - 1; i++) { Etmp= queueA.poll(); queueA.offer(tmp); } Etmp= queueA.poll(); queueA.offer(tmp); return tmp; }
@Override publicvoidclear() { queueA.clear(); }
@Override public Iterator<E> iterator() { return queueA.iterator(); }
@Override public String toString() { return queueA.toString(); } }
@Override public E poll() { if (isEmpty()) { thrownewIllegalArgumentException("queue is null"); } while (stackA.size() != 1) { stackB.push(stackA.pop()); } Eret= stackA.pop();
while (!stackB.isEmpty()) { stackA.push(stackB.pop()); } return ret; }
@Override public E element() { if (isEmpty()) { thrownewIllegalArgumentException("queue is null"); } while (stackA.size() != 1) { stackB.push(stackA.pop()); } Eret= stackA.peek();
while (!stackB.isEmpty()) { stackA.push(stackB.pop()); } return ret; }
@Override publicbooleanhasNext() { return cur != rear; }
@Override public E next() { Eret= data[cur]; cur = (cur + 1) % data.length; return ret; } } }
注意
底层实现的循环逻辑 当 Rear 到队尾时 进行循环至队首 解决空间浪费问题
注意 队列满足的判空判满条件同为 (Rear + 1) % n == Front 导致歧义 模糊定义 故而需要定义 Rear 尾指针始终指向null 从而解决歧义问题 此时判满条件为(Rear + 1) % n == Front判满条件为Rear == Front
双端队列
双端队列性质
使用头尾指针辅助操作队列
限定插入和删除操作在表两端进行操作的线性表 –表头和表尾都可以进行插入和删除
同时具有队列和栈性质的一种数据结构
本质上是循环队列的一种升级
插入与删除的时间复杂度都是 O(1)
手写代码实现
Dequeue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
package P1.Interface;
publicinterfaceDequeue<E> extendsQueue<E> { //表首添加 publicvoidaddFirst(E element); //表尾添加 publicvoidaddLast(E element); //表首删除 public E removeFirst(); //表尾删除 public E reomveLast(); //查看表首 public E getFirst(); //查看表尾 public E getLast(); }
//创建玩家的集合 LinkedSinglyCircularList<ArrayList<String>> list = newLinkedSinglyCircularList<>(); //创建玩家对应的对象ArrayList for (inti=0; i < playerCount; i++) { list.add(newArrayList<>()); }
//开始玩家的索引 intindex= beginPlayer - 1;
//将数字依次分给每个玩家 for (intnum= beginNumber; num <= maxNumber; num++) { // String answer = getAnswer(num); list.get(index++ % playerCount).add(getAnswer(num)); }
for (inti=0; i < list.size(); i++ ) { System.out.println("第" + (i + 1) + "位玩家:" + list.get(i)); } }
@Override public E remove(int index) { if (index < 0 || index >= size) { thrownewIllegalArgumentException("remove index out of range"); } Eret=null; Node node; if (size == 1) { ret = head.data; head = null; tail = null; } elseif (index == 0) { ret = head.data;; node = head.next; head.next = null; node.pre = head.pre; head.pre = null; head = node; tail.next = node;
} elseif (index == size - 1) { ret = tail.data; node = tail.pre; tail.pre = null; node.next = tail.next; tail.next = null; tail = node; head.pre = node; } else { Node p, q, r; if (index <= size / 2) { p = head; for (inti=0; i < index - 1; i++) { p = p.next; } q = p.next; r = q.next; ret = q.data; p.next = r; r.pre = p; q.next = null; q.pre = null; } else { p = tail; for (inti= size - 1; i > index + 1; i--) { p = p.pre; } q = p.pre; r = q.pre; ret = q.data; r.next = p; p.pre = r; q.next = null; q.pre = null; } } size--; return ret; }
@Override public E get(int index) { if (index < 0 || index >= size) { thrownewIllegalArgumentException("get index out of range"); } if (index == 0) { return head.data; } elseif (index == size - 1) { return tail.data; } else { Nodep= head; for (inti=0; i < index; i++) { p = p.next; } return p.data; } }
@Override public E set(int index, E element) { if (index < 0 || index >= size) { thrownewIllegalArgumentException("get index out of range"); } Eret=null; if (index == 0) { ret = head.data; head.data = element; } elseif (index == size - 1) { ret = tail.data; tail.data = element; } else { Nodep= head; for (inti=0; i < index; i++) { p = p.next; } ret = p.data; p.data = element; } return ret; }
@Override publicintsize() { return size; }
@Override publicintindexOf(E element) { //判空的操作 在测试TreeSet时发现bug 已修改 2022.2.15 23:37 if (isEmpty()) { return -1; } Nodep= head; intindex=0; while (!p.data.equals(element)) { p = p.next; index++; if (p == head) { return -1; } } return index; }
privateclassNode { public K key; //键 public V value; //值 public Node left; public Node right; publicNode() {} publicNode(K key, V value) { this.key = key; this.value = value; }
@Override public String toString() { return"{" + key + ", " + value + "}"; } }
@Override public Set<K> keySet() { TreeSet<K> set = newTreeSet<>(); for (inti=0; i < hashTable.length; i++) { AVLTreeMap<K, V> map = hashTable[i]; for (K key : map.keySet()) { set.add(key); } } return set; }
@Override public List<V> values() { LinkedList<V> list = newLinkedList<>(); for (inti=0; i < hashTable.length; i++) { AVLTreeMap<K, V> map = hashTable[i]; for (V value : map.values()) { list.add(value); } } return list; }
@Override public Set<Entry<K, V>> entrySet() { TreeSet<Entry<K, V>> set = newTreeSet<>(); for (inti=0; i < hashTable.length; i++) { AVLTreeMap<K, V> map = hashTable[i]; for (Entry entry : map.entrySet()) { set.add(entry); } } return set; } }
//定义二分搜索树的结点信息 privateclassNode { public E e; //数据域 public Node left; //左孩子(当前node结点左子树的根) public Node right; //右孩子(当前node结点右子树的根) // public int count = 1; //记录元素出现的次数 默认为1 publicNode(E e) { this.e = e; left = null; right = null; }
@Override public String toString() { return e.toString(); } }
public E findMax() { if (data.isEmpty()) { thrownewIllegalArgumentException("maxheap is empty"); } return data.get(0); }
public E findMin() { if (data.isEmpty()) { thrownewIllegalArgumentException("maxheap is empty"); } Emin= data.get(0); for (inti=1; i < data.size(); i++) { if (data.get(i).compareTo(min) < 0) { min = data.get(i); } } return min; }
public E extractMax() { if (data.isEmpty()) { thrownewIllegalArgumentException("maxheap is empty"); } Emax= findMax(); data.swap(0, data.size() - 1); data.remove(data.size() - 1); siftDown(0); return max; }
//替换最大值 并返回原先最大值 public E replace(E e) { Eres= findMax(); data.set(0, e); siftDown(0); return res; }
@Override public Iterator<E> iterator() { return data.iterator(); }
@Override public String toString() { return data.toString(); } }
privatestaticvoidprintBoard() { for (inti=0; i < BOARD_SIZE; i++) { for (intj=0; j < BOARD_SIZE; j++) { System.out.print(board[i][j] + "\t"); } System.out.println(); } }
//在size*size的矩阵中 以tr tc为同部分子矩阵的基点 dr dc 是特殊方格的索引 进行填充 privatestaticvoidchessBoard(int tr, int tc, int dr, int dc, int size) { if (size == 1) { return; }
<script> var str = '{"name": "兮动人","age":22}'; var obj = JSON.parse(str); console.log(obj); </script>
<script> var str = '{"name": "兮动人","age":22}'; var obj = JSON.parse(str); console.log(obj); var jsonstr = JSON.stringify(obj); console.log(jsonstr); </script>
<script> var str = "console.log('hello')"; eval(str); </script>
<script> var str = '{"name":"兮动人","age":22}'; var obj = eval("("+str+")"); console.log(obj) </script>
<script> var str = '{"name":"兮动人","age":22}'; var obj = JSON.parse(str) console.log(obj) </script>
<script> var str = '{"name":"兮动人","age":22}'; var obj = JSON.parse(str,fun); functionfun(name,value){ console.log(name+":"+value); return value } console.log(obj) </script>
<script> var str = '{"name":"兮动人","age":22}'; var obj = JSON.parse(str,fun); functionfun(name,value){ if (name == "age") value = 14; return value } console.log(obj) </script>
<script> var obj = JSON.stringify(value[, replacer[, space]]) </script>
<script> var obj = { a: 1, b: 2, c: 3, d: 4 }; console.log(obj); var jsonstr = JSON.stringify(obj,["a","b","c"]); console.log(jsonstr) var jsonstr = JSON.stringify(obj,["c","a","b"]); var jsonstr = JSON.stringify(obj,["c","a","b"]); </script>
<script> var obj = { name: "兮动人", age: 22 } console.log(obj); var jsonstr = JSON.stringify(obj); console.log(jsonstr) </script>
<script> var obj = { name: "兮动人", age: 22, a: undefined, f: function () { }, b:[function () {}] } console.log(obj); var jsonstr = JSON.stringify(obj); console.log(jsonstr) </script>
@Test publicvoidtest01() { System.out.println(operation(122, x -> x * 2)); System.out.println(operation(122, y -> y - 2)); System.out.println(operation(122, z -> z + 233)); }
public Integer operation(Integer num, MyFunction mf) { return mf.getValue(num); }
MyFunction 接口
1 2 3 4 5 6
package lambda;
@FunctionalInterface publicinterfaceMyFunction { public Integer getValue(Integer num); }
@Test publicvoidtest01() { System.out.println(operation(122, x -> x * 2)); System.out.println(operation(122, y -> y - 2)); System.out.println(operation(122, z -> z + 233)); }
public Integer operation(Integer num, MyFunction mf) { return mf.getValue(num); }
// The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. importVuefrom'vue'// 引入vue文件 importAppfrom'./App'// 引入同目录下的App.vue模块 import router from'./router'// 引入vue的路由 Vue.config.productionTip = false /* eslint-disable no-new */ newVue({ el: '#app',//定义作用范围就是index.html里的id为app的范围内 router,//引入路由 components: { App },//注册引入的组件App.vue template: '<App/>'//给Vue实例初始一个App组件作为template 相当于默认组件 })
// The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. importVuefrom'vue' importAppfrom'./App' import router from"./router"
<script> var person = { name: "lin", address: "hello world", link: "www.baidu.com" } let address = "传统js I am " + person.name + "I want to say " + person.address; console.log(address); let address2 = `ES6 I am ${person.name} I want to say ${person.address}`; console.log(address2); </script> </body> </html>